﻿using Grow.Db.Repository;
using Grow.EventBus.Cap.Enum;
using Grow.EventBus.Cap.PublisherEventData;
using Grow.EventBus.Cap.PublisherHandler;
using Grow.Module.Service;
using Grow.Scheduler.Job;

namespace Grow.EventBus.Cap.Job
{
    public class RetryPublisherEventDataJob : JobBase
    {
        private readonly EventBusOptions _eventBusOptions;
        private readonly IPublisherHandler _publisherHandler;
        private readonly IRepository<PublisherEventDataEntity> _publisherEventDataRepository;
        public RetryPublisherEventDataJob(IServiceContext serviceContext) : base(serviceContext)
        {
            _eventBusOptions = serviceContext.GetOptions<EventBusOptions>();

            _publisherHandler = _serviceContext.GetRequiredService<IPublisherHandler>();

            _publisherEventDataRepository = serviceContext.GetRequiredService<IRepository<PublisherEventDataEntity>>();

        }

        public override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            var eventDataDtos = await GetPublisherEventDataOfNeedRetryAsync(cancellationToken);
            if (eventDataDtos.Count == 0)
            {
                return;
            }
            Parallel.ForEach(eventDataDtos, eventDataDto =>
            {
                _ = Task.Factory.StartNew(async () =>
                {
                    await _publisherHandler.ExecuteAsync(eventDataDto, cancellationToken);
                }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            });
        }
        private async Task<List<PublisherEventDataDto>> GetPublisherEventDataOfNeedRetryAsync(CancellationToken cancellationToken)
        {
            var now = DateTimeOffset.Now;
            var sendAt = now.AddSeconds(-_eventBusOptions.StartRetryAfter);

            var publisherEventDataEntitys= await _publisherEventDataRepository.GetSelectRepository().AsNoTracking().Where(a =>
                a.SendAt < sendAt
                && a.EnvironmentName == _eventBusOptions.EnvironmentName
                && a.RetryCount < _eventBusOptions.RetryLimitCount
                && (a.LockEndTime < now || a.LockEndTime == null)
                && (a.EventDataStatus == EventDataStatusEnum.Wait || a.EventDataStatus == EventDataStatusEnum.Fail)
               ).Take(_eventBusOptions.RetryFailedMax)
               .ToListAsync(cancellationToken);

            return publisherEventDataEntitys.Select(a => new PublisherEventDataDto(
                      a.Id,
                      a.EventName,
                      a.EventBody,
                      a.EventHeads,
                      a.SendAt,
                      a.DelayAt
              )).ToList();
           
        }
    }
}
